#!/usr/bin/perl

#
# This program takes a tab-delimited file from MacStumbler
# (Didn't try NetStumbler, but I'd assume it works there, too) and outputs
# the vendor of the access point's PHY based on the MAC address
#
# the file containing the OUI->Vendor mapping can be found here:
# http://standards.ieee.org/regauth/oui/oui.txt
#
# lovingly hacked together by Andy Michaels lego@therac25.net
# last updated: 3/4/2008

use strict;
use warnings;
use Tie::File;

my $numArgs;
my $inputFileName;
my $ouiFileName = 'oui.txt';
my $outputFileName = 'out.txt';
my @infile;
my @ouifile;
my @outfile;
my $count = 0;
my %ouis;
my %wlanInfo = ();
my $usage = "usage: wifi-scan.pl input.file [oui.file] [output.file]\n";

#
# Input processing
#
$numArgs = $#ARGV + 1;
if ($numArgs == 0) {
    die $usage;
} else {
    $inputFileName = $ARGV[0];
}

if ($numArgs > 1) {
    $ouiFileName = $ARGV[1];
}

tie @infile, 'Tie::File', $inputFileName  or
    die "Can't open the input file: $!\n";

print "Opening OUI file.  This may take a while...\n";
tie @ouifile, 'Tie::File', $ouiFileName  or
    die "Can't open the file containing the OUIs: $!\n";
print "OUI file open and ready for processing.\n";
tie @outfile, 'Tie::File', $outputFileName  or
    die "Can't open the output file: $!\n";

#
# create a hash of vendors and OUIs
#
print "Grabbing all the OUIs.  This may take a while, it's a 1.7 MiB file.\n";
my $ouiCount = 0;
foreach my $line (@ouifile) {
    if($line =~ m/^([0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2})\s*\(hex\)\s*(.*)/) {
        $ouis{$1} = $2;
        $ouiCount++;
        if($ouiCount % 1000 == 0) {
            print "found $ouiCount OUIs...\n";
        }
    }
}
print "All OUI's stored!\n";

#
# cull all the SSIDs and MAC addresses from the input file
#
print "Processing Input file...\n";
foreach my $line (@infile) {
    if($line !~ m/^#/) {
        my @temp = split('\t', $line);
        my $phyInfo = $temp[2];
        # we're looking for lines kinda like this :
        # ( Some LAME SSID ) BSS ( 00:14:6c:7e:be:a6 )
        if($phyInfo =~ m/\( (.*) \) [IBS]* \( ([0-9A-Fa-f:]+) \)/) {
            my $ssid = $1;
            my $mac = $2;
            # the oui standard separates hex pairs by '-' and uses uppercase
            $mac =~ s/\:/-/g;
            $mac =~ tr/[a-f]/[A-F]/;
            $wlanInfo{$mac} = $ssid;
        }
    }
}
print "Found:  " . keys(%wlanInfo) . " SSIDs.\n";

#
# Associate vendors with MAC addresses
#
print "Looking up Vendors...\n";
for my $mac (sort keys %wlanInfo) {
    my $stuff;
    if($mac =~ m/^([0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2})/) {
        if(exists($ouis{$1})) {
            if(defined ($ouis{$1})) {
                $stuff = $mac . " -> " . $wlanInfo{$mac}
                . " -> " . $ouis{$1} . "\n";
            } else {
                $stuff = $mac . "\'s OUI is not associated with a vendor. \n";
            }
        } else {
            $stuff = $mac . " does not have a valid OUI. \n";
        }
    } else {
        $stuff = $mac . " does not have a well-formed OUI. \n";
    }
    push(@outfile, $stuff);
}

untie @infile;
untie @ouifile;
untie @outfile;
